/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.droidlogic.inputmethod.remote;

import com.droidlogic.inputmethod.remote.SoftKeyboard.KeyRow;

import android.content.res.Resources;
import android.view.inputmethod.EditorInfo;

import java.util.Locale;
/**
 * Switcher used to switching input mode between Chinese, English, symbol,etc.
 */
public class InputModeSwitcher {
        /**
         * User defined key code, used by soft keyboard.
         */
        private static final int USERDEF_KEYCODE_SHIFT_1 = -1;

        /**
         * User defined key code, used by soft keyboard.
         */
        private static final int USERDEF_KEYCODE_LANG_2 = -2;

        /**
         * User defined key code, used by soft keyboard.
         */
        private static final int USERDEF_KEYCODE_SYM_3 = -3;

        /**
         * User defined key code, used by soft keyboard.
         */
        public static final int USERDEF_KEYCODE_PHONE_SYM_4 = -4;

        /**
         * User defined key code, used by soft keyboard.
         */
        private static final int USERDEF_KEYCODE_MORE_SYM_5 = -5;

        /**
         * User defined key code, used by soft keyboard.
         */
        private static final int USERDEF_KEYCODE_SMILEY_6 = -6;


        /**
         * Bits used to indicate soft keyboard layout. If none bit is set, the
         * current input mode does not require a soft keyboard.
         **/
        private static final int MASK_SKB_LAYOUT = 0xf0000000;

        /**
         * A kind of soft keyboard layout. An input mode should be anded with
         * {@link #MASK_SKB_LAYOUT} to get its soft keyboard layout.
         */
        private static final int MASK_SKB_LAYOUT_QWERTY = 0x10000000;

        /**
         * A kind of soft keyboard layout. An input mode should be anded with
         * {@link #MASK_SKB_LAYOUT} to get its soft keyboard layout.
         */
        private static final int MASK_SKB_LAYOUT_SYMBOL1 = 0x20000000;

        /**
         * A kind of soft keyboard layout. An input mode should be anded with
         * {@link #MASK_SKB_LAYOUT} to get its soft keyboard layout.
         */
        private static final int MASK_SKB_LAYOUT_SYMBOL2 = 0x30000000;

        /**
         * A kind of soft keyboard layout. An input mode should be anded with
         * {@link #MASK_SKB_LAYOUT} to get its soft keyboard layout.
         */
        private static final int MASK_SKB_LAYOUT_SMILEY = 0x40000000;

        /**
         * A kind of soft keyboard layout. An input mode should be anded with
         * {@link #MASK_SKB_LAYOUT} to get its soft keyboard layout.
         */
        private static final int MASK_SKB_LAYOUT_PHONE = 0x50000000;

        /**
         * Used to indicate which language the current input mode is in. If the
         * current input mode works with a none-QWERTY soft keyboard, these bits are
         * also used to get language information. For example, a Chinese symbol soft
         * keyboard and an English one are different in an icon which is used to
         * tell user the language information. BTW, the smiley soft keyboard mode
         * should be set with {@link #MASK_LANGUAGE_CN} because it can only be
         * launched from Chinese QWERTY soft keyboard, and it has Chinese icon on
         * soft keyboard.
         */
        private static final int MASK_LANGUAGE = 0x0f000000;

        /**
         * Used to indicate the current language. An input mode should be anded with
         * {@link #MASK_LANGUAGE} to get this information.
         */
        private static final int MASK_LANGUAGE_CN = 0x01000000;

        /**
         * Used to indicate the current language. An input mode should be anded with
         * {@link #MASK_LANGUAGE} to get this information.
         */
        private static final int MASK_LANGUAGE_EN = 0x02000000;

        /**
         * Used to indicate which case the current input mode is in. For example,
         * English QWERTY has lowercase and uppercase. For the Chinese QWERTY, these
         * bits are ignored. For phone keyboard layout, these bits can be
         * {@link #MASK_CASE_UPPER} to request symbol page for phone soft keyboard.
         */
        private static final int MASK_CASE = 0x00f00000;

        /**
         * Used to indicate the current case information. An input mode should be
         * anded with {@link #MASK_CASE} to get this information.
         */
        private static final int MASK_CASE_LOWER = 0x00100000;

        /**
         * Used to indicate the current case information. An input mode should be
         * anded with {@link #MASK_CASE} to get this information.
         */
        private static final int MASK_CASE_UPPER = 0x00200000;

        /**
         * Mode for inputing English with soft keyboard.
         */
        public static final int MODE_SKB_ENGLISH = ( MASK_SKB_LAYOUT_QWERTY | MASK_LANGUAGE_EN );

        /**
         * Mode for inputing Chinese with soft keyboard.
         */
        public static final int MODE_SKB_CHINESE = ( MASK_SKB_LAYOUT_QWERTY | MASK_LANGUAGE_CN );

        /**
         * Mode for inputing basic symbols for Chinese mode with soft keyboard.
         */
        public static final int MODE_SKB_SYMBOL1_CN = ( MASK_SKB_LAYOUT_SYMBOL1 | MASK_LANGUAGE_CN );

        /**
         * Mode for inputing more symbols for Chinese mode with soft keyboard.
         */
        public static final int MODE_SKB_SYMBOL2_CN = ( MASK_SKB_LAYOUT_SYMBOL2 | MASK_LANGUAGE_CN );

        /**
         * Mode for inputing English lower characters with soft keyboard.
         */
        public static final int MODE_SKB_ENGLISH_LOWER = ( MASK_SKB_LAYOUT_QWERTY
                | MASK_LANGUAGE_EN | MASK_CASE_LOWER );

        /**
         * Mode for inputing English upper characters with soft keyboard.
         */
        public static final int MODE_SKB_ENGLISH_UPPER = ( MASK_SKB_LAYOUT_QWERTY
                | MASK_LANGUAGE_EN | MASK_CASE_UPPER );

        /**
         * Mode for inputing basic symbols for English mode with soft keyboard.
         */
        public static final int MODE_SKB_SYMBOL1_EN = ( MASK_SKB_LAYOUT_SYMBOL1 | MASK_LANGUAGE_EN );

        /**
         * Mode for inputing more symbols for English mode with soft keyboard.
         */
        public static final int MODE_SKB_SYMBOL2_EN = ( MASK_SKB_LAYOUT_SYMBOL2 | MASK_LANGUAGE_EN );

        /**
         * Mode for inputing smileys with soft keyboard.
         */
        public static final int MODE_SKB_SMILEY = ( MASK_SKB_LAYOUT_SMILEY | MASK_LANGUAGE_CN );

        /**
         * Mode for inputing phone numbers.
         */
        public static final int MODE_SKB_PHONE_NUM = ( MASK_SKB_LAYOUT_PHONE );

        /**
         * Mode for inputing phone numbers.
         */
        public static final int MODE_SKB_PHONE_SYM = ( MASK_SKB_LAYOUT_PHONE | MASK_CASE_UPPER );

        /**
         * Mode for inputing Chinese with a hardware keyboard.
         */
        public static final int MODE_HKB_CHINESE = ( MASK_LANGUAGE_CN );

        /**
         * Mode for inputing English with a hardware keyboard
         */
        public static final int MODE_HKB_ENGLISH = ( MASK_LANGUAGE_EN );

        /**
         * Unset mode.
         */
        public static final int MODE_UNSET = 0;

        /**
         * Maximum toggle states for a soft keyboard.
         */
        public static final int MAX_TOGGLE_STATES = 4;

        /**
         * The input mode for the current edit box.
         */
        private int mInputMode = MODE_UNSET;

        /**
         * Used to remember previous input mode. When user enters an edit field, the
         * previous input mode will be tried. If the previous mode can not be used
         * for the current situation (For example, previous mode is a soft keyboard
         * mode to input symbols, and we have a hardware keyboard for the current
         * situation), {@link #mRecentLauageInputMode} will be tried.
         **/
        private int mPreviousInputMode = MODE_SKB_CHINESE;

        /**
         * Used to remember recent mode to input language.
         */
        private int mRecentLauageInputMode = MODE_SKB_CHINESE;

        /**
         * Editor information of the current edit box.
         */
        private EditorInfo mEditorInfo;

        /**
         * Used to indicate required toggling operations.
         */
        private ToggleStates mToggleStates = new ToggleStates();

        /**
         * The current field is a short message field?
         */
        private boolean mShortMessageField;

        /**
         * Is return key in normal state?
         */
        private boolean mEnterKeyNormal = true;

        /**
         * Current icon. 0 for none icon.
         */
        int mInputIcon = R.drawable.ime_pinyin;

        /**
         * IME service.
         */
        private RemoteIME mImeService;

        /**
         * Key toggling state for Chinese mode.
         */
        private int mToggleStateCn;

        /**
         * Key toggling state for Chinese mode with candidates.
         */
        private int mToggleStateCnCand;

        /**
         * Key toggling state for English lowwercase mode.
         */
        private int mToggleStateEnLower;

        /**
         * Key toggling state for English upppercase mode.
         */
        private int mToggleStateEnUpper;

        /**
         * Key toggling state for English symbol mode for the first page.
         */
        private int mToggleStateEnSym1;

        /**
         * Key toggling state for English symbol mode for the second page.
         */
        private int mToggleStateEnSym2;

        /**
         * Key toggling state for smiley mode.
         */
        private int mToggleStateSmiley;

        /**
         * Key toggling state for phone symbol mode.
         */
        private int mToggleStatePhoneSym;

        /**
         * Key toggling state for GO action of ENTER key.
         */
        private int mToggleStateGo;

        /**
         * Key toggling state for SEARCH action of ENTER key.
         */
        private int mToggleStateSearch;

        /**
         * Key toggling state for SEND action of ENTER key.
         */
        private int mToggleStateSend;

        /**
         * Key toggling state for NEXT action of ENTER key.
         */
        private int mToggleStateNext;

        /**
         * Key toggling state for SEND action of ENTER key.
         */
        private int mToggleStateDone;

        /**
         * QWERTY row toggling state for Chinese input.
         */
        private int mToggleRowCn;

        /**
         * QWERTY row toggling state for English input.
         */
        private int mToggleRowEn;

        /**
         * QWERTY row toggling state for URI input.
         */
        private int mToggleRowUri;

        /**
         * QWERTY row toggling state for email address input.
         */
        private int mToggleRowEmailAddress;

        class ToggleStates {
                /**
                 * If it is true, this soft keyboard is a QWERTY one.
                 */
                boolean mQwerty;

                /**
                 * If {@link #mQwerty} is true, this variable is used to decide the
                 * letter case of the QWERTY keyboard.
                 */
                boolean mQwertyUpperCase;

                /**
                 * The id of enabled row in the soft keyboard. Refer to
                 * {@link com.droidlogic.inputmethod.remote.SoftKeyboard.KeyRow} for
                 * details.
                 */
                public int mRowIdToEnable;

                /**
                 * Used to store all other toggle states for the current input mode.
                 */
                public int mKeyStates[] = new int[MAX_TOGGLE_STATES];

                /**
                 * Number of states to toggle.
                 */
                public int mKeyStatesNum;
        }

        public InputModeSwitcher ( RemoteIME imeService ) {
            mImeService = imeService;
            if ( "zh".equals ( Locale.getDefault().getLanguage() ) ) {
                mRecentLauageInputMode = MODE_SKB_CHINESE;
            } else {
                mRecentLauageInputMode = MODE_SKB_ENGLISH;
            }
            Resources r = mImeService.getResources();
            mToggleStateCn = Integer.parseInt ( r.getString ( R.string.toggle_cn ) );
            mToggleStateCnCand = Integer.parseInt ( r
                                                    .getString ( R.string.toggle_cn_cand ) );
            mToggleStateEnLower = Integer.parseInt ( r
                                  .getString ( R.string.toggle_en_lower ) );
            mToggleStateEnUpper = Integer.parseInt ( r
                                  .getString ( R.string.toggle_en_upper ) );
            mToggleStateEnSym1 = Integer.parseInt ( r
                                                    .getString ( R.string.toggle_en_sym1 ) );
            mToggleStateEnSym2 = Integer.parseInt ( r
                                                    .getString ( R.string.toggle_en_sym2 ) );
            mToggleStateSmiley = Integer.parseInt ( r
                                                    .getString ( R.string.toggle_smiley ) );
            mToggleStatePhoneSym = Integer.parseInt ( r
                                   .getString ( R.string.toggle_phone_sym ) );
            mToggleStateGo = Integer
                             .parseInt ( r.getString ( R.string.toggle_enter_go ) );
            mToggleStateSearch = Integer.parseInt ( r
                                                    .getString ( R.string.toggle_enter_search ) );
            mToggleStateSend = Integer.parseInt ( r
                                                  .getString ( R.string.toggle_enter_send ) );
            mToggleStateNext = Integer.parseInt ( r
                                                  .getString ( R.string.toggle_enter_next ) );
            mToggleStateDone = Integer.parseInt ( r
                                                  .getString ( R.string.toggle_enter_done ) );
            mToggleRowCn = Integer.parseInt ( r.getString ( R.string.toggle_row_cn ) );
            mToggleRowEn = Integer.parseInt ( r.getString ( R.string.toggle_row_en ) );
            mToggleRowUri = Integer.parseInt ( r.getString ( R.string.toggle_row_uri ) );
            mToggleRowEmailAddress = Integer.parseInt ( r
                                     .getString ( R.string.toggle_row_emailaddress ) );
        }

        public int getInputMode() {
            return mInputMode;
        }

        public ToggleStates getToggleStates() {
            return mToggleStates;
        }

        public int getSkbLayout() {
            int layout = ( mInputMode & MASK_SKB_LAYOUT );
            switch ( layout ) {
                case MASK_SKB_LAYOUT_QWERTY:
                    return R.xml.skb_qwerty;
                case MASK_SKB_LAYOUT_SYMBOL1:
                    return R.xml.skb_sym1;
                case MASK_SKB_LAYOUT_SYMBOL2:
                    return R.xml.skb_sym2;
                case MASK_SKB_LAYOUT_SMILEY:
                    return R.xml.skb_smiley;
                case MASK_SKB_LAYOUT_PHONE:
                    return R.xml.skb_phone;
            }
            return 0;
        }

        // Return the icon to update.
        public int switchLanguageWithHkb() {
            int newInputMode = MODE_HKB_CHINESE;
            mInputIcon = R.drawable.ime_pinyin;
            if ( MODE_HKB_CHINESE == mInputMode ) {
                newInputMode = MODE_HKB_ENGLISH;
                mInputIcon = R.drawable.ime_en;
            }
            saveInputMode ( newInputMode );
            return mInputIcon;
        }

        // Return the icon to update.
        public int switchModeForUserKey ( int userKey ) {
            int newInputMode = MODE_UNSET;
            if ( USERDEF_KEYCODE_LANG_2 == userKey ) {
                if ( MODE_SKB_CHINESE == mInputMode ) {
                    newInputMode = MODE_SKB_ENGLISH_LOWER;
                } else if ( MODE_SKB_ENGLISH_LOWER == mInputMode
                            || MODE_SKB_ENGLISH_UPPER == mInputMode ) {
                    newInputMode = MODE_SKB_CHINESE;
                } else if ( MODE_SKB_SYMBOL1_CN == mInputMode ) {
                    newInputMode = MODE_SKB_SYMBOL1_EN;
                } else if ( MODE_SKB_SYMBOL1_EN == mInputMode ) {
                    newInputMode = MODE_SKB_SYMBOL1_CN;
                } else if ( MODE_SKB_SYMBOL2_CN == mInputMode ) {
                    newInputMode = MODE_SKB_SYMBOL2_EN;
                } else if ( MODE_SKB_SYMBOL2_EN == mInputMode ) {
                    newInputMode = MODE_SKB_SYMBOL2_CN;
                } else if ( MODE_SKB_SMILEY == mInputMode ) {
                    newInputMode = MODE_SKB_CHINESE;
                }
            } else if ( USERDEF_KEYCODE_SYM_3 == userKey ) {
                if ( MODE_SKB_CHINESE == mInputMode ) {
                    newInputMode = MODE_SKB_SYMBOL1_CN;
                } else if ( MODE_SKB_ENGLISH_UPPER == mInputMode
                            || MODE_SKB_ENGLISH_LOWER == mInputMode ) {
                    newInputMode = MODE_SKB_SYMBOL1_EN;
                } else if ( MODE_SKB_SYMBOL1_EN == mInputMode
                            || MODE_SKB_SYMBOL2_EN == mInputMode ) {
                    newInputMode = MODE_SKB_ENGLISH_LOWER;
                } else if ( MODE_SKB_SYMBOL1_CN == mInputMode
                            || MODE_SKB_SYMBOL2_CN == mInputMode ) {
                    newInputMode = MODE_SKB_CHINESE;
                } else if ( MODE_SKB_SMILEY == mInputMode ) {
                    newInputMode = MODE_SKB_SYMBOL1_CN;
                }
            } else if ( USERDEF_KEYCODE_SHIFT_1 == userKey ) {
                if ( MODE_SKB_ENGLISH_LOWER == mInputMode ) {
                    newInputMode = MODE_SKB_ENGLISH_UPPER;
                } else if ( MODE_SKB_ENGLISH_UPPER == mInputMode ) {
                    newInputMode = MODE_SKB_ENGLISH_LOWER;
                }
            } else if ( USERDEF_KEYCODE_MORE_SYM_5 == userKey ) {
                int sym = ( MASK_SKB_LAYOUT & mInputMode );
                if ( MASK_SKB_LAYOUT_SYMBOL1 == sym ) {
                    sym = MASK_SKB_LAYOUT_SYMBOL2;
                } else {
                    sym = MASK_SKB_LAYOUT_SYMBOL1;
                }
                newInputMode = ( ( mInputMode & ( ~MASK_SKB_LAYOUT ) ) | sym );
            } else if ( USERDEF_KEYCODE_SMILEY_6 == userKey ) {
                if ( MODE_SKB_CHINESE == mInputMode ) {
                    newInputMode = MODE_SKB_SMILEY;
                } else {
                    newInputMode = MODE_SKB_CHINESE;
                }
            } else if ( USERDEF_KEYCODE_PHONE_SYM_4 == userKey ) {
                if ( MODE_SKB_PHONE_NUM == mInputMode ) {
                    newInputMode = MODE_SKB_PHONE_SYM;
                } else {
                    newInputMode = MODE_SKB_PHONE_NUM;
                }
            }
            if ( newInputMode == mInputMode || MODE_UNSET == newInputMode ) {
                return mInputIcon;
            }
            saveInputMode ( newInputMode );
            prepareToggleStates ( true );
            return mInputIcon;
        }

        // Return the icon to update.
        public int requestInputWithHkb ( EditorInfo editorInfo ) {
            mShortMessageField = false;
            boolean english = false;
            int newInputMode = MODE_HKB_CHINESE;
            switch ( editorInfo.inputType & EditorInfo.TYPE_MASK_CLASS ) {
                case EditorInfo.TYPE_CLASS_NUMBER:
                case EditorInfo.TYPE_CLASS_PHONE:
                case EditorInfo.TYPE_CLASS_DATETIME:
                    english = true;
                    break;
                case EditorInfo.TYPE_CLASS_TEXT:
                    int v = editorInfo.inputType & EditorInfo.TYPE_MASK_VARIATION;
                    if ( v == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
                            || v == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD
                            || v == EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
                            || v == EditorInfo.TYPE_TEXT_VARIATION_URI ) {
                        english = true;
                    } else if ( v == EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE ) {
                        mShortMessageField = true;
                    }
                    break;
                default:
            }
            if ( english ) {
                // If the application request English mode, we switch to it.
                newInputMode = MODE_HKB_ENGLISH;
            } else {
                // If the application do not request English mode, we will
                // try to keep the previous mode to input language text.
                // Because there is not soft keyboard, we need discard all
                // soft keyboard related information from the previous language
                // mode.
                if ( ( mRecentLauageInputMode & MASK_LANGUAGE ) == MASK_LANGUAGE_CN ) {
                    newInputMode = MODE_HKB_CHINESE;
                } else {
                    newInputMode = MODE_HKB_ENGLISH;
                }
            }
            mEditorInfo = editorInfo;
            saveInputMode ( newInputMode );
            prepareToggleStates ( false );
            return mInputIcon;
        }

        // Return the icon to update.
        public int requestInputWithSkb ( EditorInfo editorInfo ) {
            mShortMessageField = false;
            int newInputMode = MODE_SKB_CHINESE;
            switch ( editorInfo.inputType & EditorInfo.TYPE_MASK_CLASS ) {
                case EditorInfo.TYPE_CLASS_NUMBER:
                case EditorInfo.TYPE_CLASS_DATETIME:
                    newInputMode = MODE_SKB_SYMBOL1_EN;
                    break;
                case EditorInfo.TYPE_CLASS_PHONE:
                    newInputMode = MODE_SKB_PHONE_NUM;
                    break;
                case EditorInfo.TYPE_CLASS_TEXT:
                    int v = editorInfo.inputType & EditorInfo.TYPE_MASK_VARIATION;
                    if ( v == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
                            || v == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD
                            || v == EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
                            || v == EditorInfo.TYPE_TEXT_VARIATION_URI ) {
                        // If the application request English mode, we switch to it.
                        newInputMode = MODE_SKB_ENGLISH_LOWER;
                    } else {
                        if ( v == EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE ) {
                            mShortMessageField = true;
                        }
                        // If the application do not request English mode, we will
                        // try to keep the previous mode.
                        int skbLayout = ( mInputMode & MASK_SKB_LAYOUT );
                        newInputMode = mInputMode;
                        if ( 0 == skbLayout ) {
                            if ( ( mInputMode & MASK_LANGUAGE ) == MASK_LANGUAGE_CN ) {
                                newInputMode = MODE_SKB_CHINESE;
                            } else {
                                newInputMode = MODE_SKB_ENGLISH_LOWER;
                            }
                        }
                    }
                    break;
                default:
                    // Try to keep the previous mode.
                    int skbLayout = ( mInputMode & MASK_SKB_LAYOUT );
                    newInputMode = mInputMode;
                    if ( 0 == skbLayout ) {
                        if ( ( mInputMode & MASK_LANGUAGE ) == MASK_LANGUAGE_CN ) {
                            newInputMode = MODE_SKB_CHINESE;
                        } else {
                            newInputMode = MODE_SKB_ENGLISH_LOWER;
                        }
                    }
                    break;
            }
            mEditorInfo = editorInfo;
            saveInputMode ( newInputMode );
            prepareToggleStates ( true );
            return mInputIcon;
        }

        // Return the icon to update.
        public int requestBackToPreviousSkb() {
            int layout = ( mInputMode & MASK_SKB_LAYOUT );
            int lastLayout = ( mPreviousInputMode & MASK_SKB_LAYOUT );
            if ( 0 != layout && 0 != lastLayout ) {
                mInputMode = mPreviousInputMode;
                saveInputMode ( mInputMode );
                prepareToggleStates ( true );
                return mInputIcon;
            }
            return 0;
        }

        public int getTooggleStateForCnCand() {
            return mToggleStateCnCand;
        }

        public boolean isEnglishWithHkb() {
            return MODE_HKB_ENGLISH == mInputMode;
        }

        public boolean isEnglishWithSkb() {
            return MODE_SKB_ENGLISH_LOWER == mInputMode
                   || MODE_SKB_ENGLISH_UPPER == mInputMode;
        }

        public boolean isEnglishUpperCaseWithSkb() {
            return MODE_SKB_ENGLISH_UPPER == mInputMode;
        }

        public boolean isChineseText() {
            int skbLayout = ( mInputMode & MASK_SKB_LAYOUT );
            if ( MASK_SKB_LAYOUT_QWERTY == skbLayout || 0 == skbLayout ) {
                int language = ( mInputMode & MASK_LANGUAGE );
                if ( MASK_LANGUAGE_CN == language ) { return true; }
            }
            return false;
        }

        public boolean isChineseTextWithHkb() {
            int skbLayout = ( mInputMode & MASK_SKB_LAYOUT );
            if ( 0 == skbLayout ) {
                int language = ( mInputMode & MASK_LANGUAGE );
                if ( MASK_LANGUAGE_CN == language ) { return true; }
            }
            return false;
        }

        public boolean isChineseTextWithSkb() {
            int skbLayout = ( mInputMode & MASK_SKB_LAYOUT );
            if ( MASK_SKB_LAYOUT_QWERTY == skbLayout ) {
                int language = ( mInputMode & MASK_LANGUAGE );
                if ( MASK_LANGUAGE_CN == language ) { return true; }
            }
            return false;
        }

        public boolean isSymbolWithSkb() {
            int skbLayout = ( mInputMode & MASK_SKB_LAYOUT );
            if ( MASK_SKB_LAYOUT_SYMBOL1 == skbLayout
                    || MASK_SKB_LAYOUT_SYMBOL2 == skbLayout ) {
                return true;
            }
            return false;
        }

        public boolean isEnterNoramlState() {
            return mEnterKeyNormal;
        }

        public boolean tryHandleLongPressSwitch ( int keyCode ) {
            if ( USERDEF_KEYCODE_LANG_2 == keyCode
                    || USERDEF_KEYCODE_PHONE_SYM_4 == keyCode ) {
                mImeService.showOptionsMenu();
                return true;
            }
            return false;
        }

        private void saveInputMode ( int newInputMode ) {
            mPreviousInputMode = mInputMode;
            mInputMode = newInputMode;
            int skbLayout = ( mInputMode & MASK_SKB_LAYOUT );
            if ( MASK_SKB_LAYOUT_QWERTY == skbLayout || 0 == skbLayout ) {
                mRecentLauageInputMode = mInputMode;
            }
            mInputIcon = R.drawable.ime_pinyin;
            if ( isEnglishWithHkb() ) {
                mInputIcon = R.drawable.ime_en;
            } else if ( isChineseTextWithHkb() ) {
                mInputIcon = R.drawable.ime_pinyin;
            }
            if ( !Environment.getInstance().hasHardKeyboard() ) {
                mInputIcon = 0;
            }
        }

        private void prepareToggleStates ( boolean needSkb ) {
            mEnterKeyNormal = true;
            if ( !needSkb ) { return; }
            mToggleStates.mQwerty = false;
            mToggleStates.mKeyStatesNum = 0;
            int states[] = mToggleStates.mKeyStates;
            int statesNum = 0;
            // Toggle state for language.
            int language = ( mInputMode & MASK_LANGUAGE );
            int layout = ( mInputMode & MASK_SKB_LAYOUT );
            int charcase = ( mInputMode & MASK_CASE );
            int variation = mEditorInfo.inputType & EditorInfo.TYPE_MASK_VARIATION;
            if ( MASK_SKB_LAYOUT_PHONE != layout ) {
                if ( MASK_LANGUAGE_CN == language ) {
                    // Chinese and Chinese symbol are always the default states,
                    // do not add a toggling operation.
                    if ( MASK_SKB_LAYOUT_QWERTY == layout ) {
                        mToggleStates.mQwerty = true;
                        mToggleStates.mQwertyUpperCase = true;
                        if ( mShortMessageField ) {
                            states[statesNum] = mToggleStateSmiley;
                            statesNum++;
                        }
                    }
                } else if ( MASK_LANGUAGE_EN == language ) {
                    if ( MASK_SKB_LAYOUT_QWERTY == layout ) {
                        mToggleStates.mQwerty = true;
                        mToggleStates.mQwertyUpperCase = false;
                        states[statesNum] = mToggleStateEnLower;
                        if ( MASK_CASE_UPPER == charcase ) {
                            mToggleStates.mQwertyUpperCase = true;
                            states[statesNum] = mToggleStateEnUpper;
                        }
                        statesNum++;
                    } else if ( MASK_SKB_LAYOUT_SYMBOL1 == layout ) {
                        states[statesNum] = mToggleStateEnSym1;
                        statesNum++;
                    } else if ( MASK_SKB_LAYOUT_SYMBOL2 == layout ) {
                        states[statesNum] = mToggleStateEnSym2;
                        statesNum++;
                    }
                }
                // Toggle rows for QWERTY.
                mToggleStates.mRowIdToEnable = KeyRow.DEFAULT_ROW_ID;
                if ( variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS ) {
                    mToggleStates.mRowIdToEnable = mToggleRowEmailAddress;
                } else if ( variation == EditorInfo.TYPE_TEXT_VARIATION_URI ) {
                    mToggleStates.mRowIdToEnable = mToggleRowUri;
                } else if ( MASK_LANGUAGE_CN == language ) {
                    mToggleStates.mRowIdToEnable = mToggleRowCn;
                } else if ( MASK_LANGUAGE_EN == language ) {
                    mToggleStates.mRowIdToEnable = mToggleRowEn;
                }
            } else {
                if ( MASK_CASE_UPPER == charcase ) {
                    states[statesNum] = mToggleStatePhoneSym;
                    statesNum++;
                }
            }
            // Toggle state for enter key.
            int action = mEditorInfo.imeOptions
                         & ( EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION );
            if ( action == EditorInfo.IME_ACTION_GO ) {
                states[statesNum] = mToggleStateGo;
                statesNum++;
                mEnterKeyNormal = false;
            } else if ( action == EditorInfo.IME_ACTION_SEARCH ) {
                states[statesNum] = mToggleStateSearch;
                statesNum++;
                mEnterKeyNormal = false;
            } else if ( action == EditorInfo.IME_ACTION_SEND ) {
                states[statesNum] = mToggleStateSend;
                statesNum++;
                mEnterKeyNormal = false;
            } else if ( action == EditorInfo.IME_ACTION_NEXT ) {
                int f = mEditorInfo.inputType & EditorInfo.TYPE_MASK_FLAGS;
                if ( f != EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE ) {
                    states[statesNum] = mToggleStateNext;
                    statesNum++;
                    mEnterKeyNormal = false;
                }
            } else if ( action == EditorInfo.IME_ACTION_DONE ) {
                states[statesNum] = mToggleStateDone;
                statesNum++;
                mEnterKeyNormal = false;
            }
            mToggleStates.mKeyStatesNum = statesNum;
        }
}
